using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace RunAllTestsInSolutionTask
{
    public class ManagedProjectParser : BaseProjectParser
    {


        public ManagedProjectParser(string projectFileName) :
            base(projectFileName)
        {
        }

        public override bool IsTestProject()
        {
            var ans = from projectType in _projectXml.Descendants()
                      where (projectType.Name.LocalName == "ProjectTypes")
                      select projectType;
            foreach (var type in ans)
            {
                if (type.Value.Contains(TEST_TYPE_GUID))
                    return true;

            }
            return false;
        }

        public override string GetOutputFile(string configuration)
        {
            if (string.IsNullOrEmpty(configuration))
            {
                configuration = "Win32|Debug";
            }

            var projectXml = XDocument.Load(_projectFileName);
            try
            {
                var outputFile = GetOutPutFileElement(projectXml, configuration);
                var path = Path.GetDirectoryName(_projectFileName);

                return Path.Combine(path,outputFile.Value);
            }
            catch (InvalidOperationException)
            //user didnt override outputfile so we continue with the defaults..
            {
                string outdir = GetOutdir(projectXml, configuration);
                string TargetName = GetTargetName(projectXml, configuration);
                string TargetExtension = GetTargetExtension(projectXml, configuration);
                return Path.Combine(outdir, TargetName + TargetExtension);
            }
        }
        private static XElement GetOutPutFileElement(XDocument projectXml, string configuration)
        {
            var outputfiles = from elem in projectXml.Descendants()
                              where elem.Name.LocalName == "OutputFile"
                              select elem;

            var outputFile = (from path in outputfiles
                              where path.Parent.Parent.Attribute("Condition").Value.Contains(configuration)
                              select path).Single();
            //these are ,like the defualts
            if (outputFile.Value == "$(OutDir)$(TargetName)$(TargetExt)")
                throw new InvalidOperationException();
              
            return outputFile;
        }

        private string GetOutdir(XDocument projectXml, string configuration)
        {
            var projectPath = Path.GetDirectoryName(_projectFileName);
            try
            {
                var outdirElement = GetOutDirElement(projectXml, configuration);
                return Path.Combine(projectPath, outdirElement.Value);
            }
            catch (InvalidOperationException ex)
            {
                //by default outdir is set by visual studio to: $(SolutionDir)$(Configuration)\
                var outdir = Path.Combine(projectPath, @"..\");
                outdir = Path.Combine(outdir, configuration);
                return outdir;
            }
        }

        private static XElement GetOutDirElement(XDocument projectXml, string configuration)
        {
            var outputPaths = from elem in projectXml.Descendants()
                              where elem.Name.LocalName == "OutDir"
                              select elem;

            var outputPath = (from path in outputPaths
                              where path.ElementInConfiguration(configuration)
                              select path).Single();
            return outputPath;
        }

   
        private string GetTargetName(XDocument projectXml, string configuration)
        {
            try
            {
                var targetElement = GetTargetElement(projectXml, configuration);
                return targetElement.Value;

            }
            catch (InvalidOperationException ex)
            {
                var TargetName = Path.GetFileNameWithoutExtension(_projectFileName);
                return TargetName;
            }
        }

        private static XElement GetTargetElement(XDocument projectXml, string configuration)
        {
            var outputPaths = from elem in projectXml.Descendants()
                              where elem.Name.LocalName == "TargetName"
                              select elem;

            var outputPath = (from path in outputPaths
                              where path.ElementInConfiguration(configuration)
                              select path).Single();
            return outputPath;
        }

        private string GetTargetExtension(XDocument projectXml, string configuration)
        {
            var targetElement = GetConfigurationTypeElement(projectXml, configuration);

            if (targetElement.Value == "Application")
                return ".exe";
            else 
                return ".dll";
        }

        private static XElement GetConfigurationTypeElement(XDocument projectXml, string configuration)
        {
            var outputPaths = from elem in projectXml.Descendants()
                              where elem.Name.LocalName == "ConfigurationType"
                              select elem;

            var outputPath = (from path in outputPaths
                              where path.ElementInConfiguration(configuration)
                              select path).Single();
            return outputPath;
        }
    }


    public static class XMLExtensions
    {
        public static bool ElementInConfiguration(this XElement elem, string Configuration)
        {
            if ((elem.HasCondition()) && elem.GetCondition().Contains(Configuration))
                return true;
            if ((elem.Parent.HasCondition()) && elem.Parent.GetCondition().Contains(Configuration))
                return true;
            if (elem.Parent.IsGlobalProperties())
                return true;
            return false;

        }

        public static bool HasCondition(this XElement element)
        {
            var cond = element.Attribute("Condition");
            return cond != null;
        }

        public static string GetCondition(this XElement element)
        {
            return element.Attribute("Condition").Value;
        }
        public static bool IsGlobalProperties(this XElement element)
        {
            if ( (element.Name.LocalName == "PropertyGroup") &&
                 (element.Attribute("Label")!=null) &&
                 (element.Attribute("Label").Value == "Globals") )
            {
                return true;
            }
            return false;
        }
        
    }

}
